package sojo.com.inverter.modbus;

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
import com.serotonin.modbus4j.sero.util.queue.ByteQueue;
import lombok.extern.slf4j.Slf4j;
import sojo.com.inverter.utils.MyTcpListener;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * @author cheng_hu
 * @description:
 * @date 2023-02-22 14:57:32
 */
@Slf4j
public class ModbusRTUClient implements ModbusClient{


    private static final ModbusFactory modbusFactory = new ModbusFactory();

    private ModbusMaster rtuMaster = null;

    /**
     * 启动端口，让客户端连接
     * MODBUS RTU
     */
    public ModbusRTUClient(String host, int port) throws ModbusInitException {
        IpParameters params = new IpParameters();
        params.setHost(host);
        params.setPort(port);
        // true RTU false TCP
        params.setEncapsulated(false);
        this.rtuMaster = modbusFactory.createTcpListener(params);// TCP 协议
        //设置超时时间
        this.rtuMaster.setTimeout(2000);
        //设置重连次数
        this.rtuMaster.setRetries(3);
        //初始化
        this.rtuMaster.init();
        log.info("Modbus TCP Initialized = {} Connected = {}", rtuMaster.isInitialized(), rtuMaster.isConnected());

//        IpParameters params = new IpParameters();
//        params.setHost(host);
//        params.setPort(port);
//        // true RTU false TCP
//        params.setEncapsulated(false);
//        MyTcpListener  tcpListener = new MyTcpListener(params);
//        //设置超时时间
//        tcpListener.setTimeout(2000);
//        //设置重连次数
//        tcpListener.setRetries(3);
//        //初始化
//        tcpListener.init();
    }

    /**
     * 连接客户端端口
     * MODBUS TCP
     */
    public ModbusRTUClient(String host, int port, boolean keepAlive) throws ModbusInitException {
        IpParameters params = new IpParameters();
        params.setHost(host);
        params.setPort(port);
        params.setEncapsulated(false);// true RTU
        this.rtuMaster = modbusFactory.createTcpMaster(params, keepAlive);// TCP 协议
        //设置超时时间
        this.rtuMaster.setTimeout(2000);
        //设置重连次数
        this.rtuMaster.setRetries(3);
        //初始化
        this.rtuMaster.init();
        log.info("Modbus TCP Initialized = {} Connected = {}", rtuMaster.isInitialized(), rtuMaster.isConnected());
    }

    @PostConstruct
    public void init() {
        log.info("ModbusTCPClientStation PostConstruct");
    }

    @PreDestroy
    public void destroy() {
        this.rtuMaster.destroy();
        log.info("ModbusTCPClientStation PreDestroy");
    }


    public void aa(){

    }

    /**
     * 读02功能码
     * 读取保持寄存器数据
     *
     * @param slaveId
     * @param offset       起始位置
     * @param numberOfBits 需要读取的长度
     * @return
     */
    public short[] readDiscreteInputs(int slaveId, int offset, int numberOfBits) {
        try {
            ReadDiscreteInputsRequest request = new ReadDiscreteInputsRequest(slaveId, offset, numberOfBits);
            ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) rtuMaster.send(request);
            return response.getShortData();
        } catch (Exception e) {
            log.error("读取数据异常！");
        }
        return null;
    }

    /**
     * 读03功能码
     * 读取保持寄存器数据
     *
     * @param slaveId
     * @param offset       起始位置
     * @param numberOfBits 需要读取的长度
     * @return
     */
    public short[] readHoldingRegister(int slaveId, int offset, int numberOfBits) {
        try {
            ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, offset, numberOfBits);
            ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) rtuMaster.send(request);
            return response.getShortData();
        } catch (Exception e) {
            log.error("读取数据异常！");
        }
        return null;
    }

    /**
     * 读取保持寄存器 功能码[03]
     *
     * @param offset     开始地址
     * @param numberOfBits 读取数量
     * @return
     * @throws ModbusInitException
     */
    public byte[] modbusTCP03(int slaveId, int offset, int numberOfBits) {
        try {
            //发送请求
            ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, offset, numberOfBits);
            ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) rtuMaster.send(request);
            if (response.isException()) {
                log.error("读取数据异常！");
                return null;
            } else {
                ByteQueue byteQueue = new ByteQueue();
                response.write(byteQueue);
                log.info("寄存器开始地址:{} 功能码:{} 从站地址:{} 收到的响应信息大小:{} 收到的响应信息值:{}", offset, response.getFunctionCode(), response.getSlaveId(), byteQueue.size(), byteQueue);
                byte[] bytes = byteQueue.peekAll();
                byte[] bytes1 = new byte[byteQueue.size()-3];
                //截取功能码和从站地址
                System.arraycopy(bytes,3,bytes1,0,bytes1.length);
                return bytes1;
            }
        } catch (Exception e) {
            log.error("读取数据异常！" + e.getMessage());
        }
        return null;
    }

    /**
     * 读取[03 Holding Register类型 2x]模拟量数据
     * @param slaveId  slave Id
     * @param offset   位置
     * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
     */
    public Number readHoldingRegister03(int slaveId, int offset, int dataType) {
        // 03 Holding Register类型数据读取
        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
        Number value = null;
        try {
            value = rtuMaster.getValue(loc);
        } catch (Exception e) {
            log.error("读取数据异常！");
        }
        return value;
    }

    /**
     * 批量读取使用方法*
     *
     * @param num      地址递增量*
     * @param id       批量数量
     * @param slaveId  设备地址
     * @param offset   寄存器地址
     * @param dataType 类型
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public BatchResults<Integer> batchRead(int num, int id, int slaveId, int offset, int dataType) {

        BatchRead<Integer> batch = new BatchRead<Integer>();
//        batch.addLocator(1, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
        for (int i = 0; i < id; i++) {
            batch.addLocator(i, BaseLocator.holdingRegister(slaveId, offset, dataType));
            offset += num;
        }
        batch.setContiguousRequests(false);
        BatchResults<Integer> results = null;
        try {
            results = rtuMaster.send(batch);
//            System.out.println(results.getValue(0));
        } catch (Exception e) {
            log.error("读取数据异常！");
        }
        return results;
    }

}
